using System;

namespace Lucene.Net.Search
{
    /// <summary>
    /// Copyright 2004 The Apache Software Foundation
    ///
    /// Licensed under the Apache License, Version 2.0 (the "License");
    /// you may not use this file except in compliance with the License.
    /// You may obtain a copy of the License at
    ///
    ///     http://www.apache.org/licenses/LICENSE-2.0
    ///
    /// Unless required by applicable law or agreed to in writing, software
    /// distributed under the License is distributed on an "AS IS" BASIS,
    /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    /// See the License for the specific language governing permissions and
    /// limitations under the License.
    /// </summary>

    /// <summary>
    /// The Scorer for DisjunctionMaxQuery.  The union of all documents generated by the the subquery scorers
    /// is generated in document number order.  The score for each document is the maximum of the scores computed
    /// by the subquery scorers that generate that document, plus tieBreakerMultiplier times the sum of the scores
    /// for the other subqueries that generate the document.
    /// </summary>
    internal class DisjunctionMaxScorer : DisjunctionScorer
    {
        /* Multiplier applied to non-maximum-scoring subqueries for a document as they are summed into the result. */
        private readonly float TieBreakerMultiplier;
        private int Freq_Renamed = -1;

        /* Used when scoring currently matching doc. */
        private float ScoreSum;
        private float ScoreMax;

        /// <summary>
        /// Creates a new instance of DisjunctionMaxScorer
        /// </summary>
        /// <param name="weight">
        ///          The Weight to be used. </param>
        /// <param name="tieBreakerMultiplier">
        ///          Multiplier applied to non-maximum-scoring subqueries for a
        ///          document as they are summed into the result. </param>
        /// <param name="subScorers">
        ///          The sub scorers this Scorer should iterate on </param>
        public DisjunctionMaxScorer(Weight weight, float tieBreakerMultiplier, Scorer[] subScorers)
            : base(weight, subScorers)
        {
            this.TieBreakerMultiplier = tieBreakerMultiplier;
        }

        /// <summary>
        /// Determine the current document score.  Initially invalid, until <seealso cref="#nextDoc()"/> is called the first time. </summary>
        /// <returns> the score of the current generated document </returns>
        public override float Score()
        {
            return ScoreMax + (ScoreSum - ScoreMax) * TieBreakerMultiplier;
        }

        protected internal override void AfterNext()
        {
            Doc = SubScorers[0].DocID();
            if (Doc != NO_MORE_DOCS)
            {
                ScoreSum = ScoreMax = SubScorers[0].Score();
                Freq_Renamed = 1;
                ScoreAll(1);
                ScoreAll(2);
            }
        }

        // Recursively iterate all subScorers that generated last doc computing sum and max
        private void ScoreAll(int root)
        {
            if (root < NumScorers && SubScorers[root].DocID() == Doc)
            {
                float sub = SubScorers[root].Score();
                Freq_Renamed++;
                ScoreSum += sub;
                ScoreMax = Math.Max(ScoreMax, sub);
                ScoreAll((root << 1) + 1);
                ScoreAll((root << 1) + 2);
            }
        }

        public override int Freq()
        {
            return Freq_Renamed;
        }
    }
}